/* ==================================================================   
 * Created [2009-4-27 下午11:32:55] by Jon.King 
 * ==================================================================  
 * TSS 
 * ================================================================== 
 * mailTo:jinpujun@hotmail.com
 * Copyright (c) Jon.King, 2009-2012 
 * ================================================================== 
 */

package com.jinhe.tss.cms.service.impl;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

import com.jinhe.tss.cms.CMSConstants;
import com.jinhe.tss.cms.dao.IArticleDao;
import com.jinhe.tss.cms.dao.IChannelDao;
import com.jinhe.tss.cms.entity.Article;
import com.jinhe.tss.cms.entity.ArticleType;
import com.jinhe.tss.cms.entity.Attachment;
import com.jinhe.tss.cms.entity.Channel;
import com.jinhe.tss.cms.entity.ChannelArticle;
import com.jinhe.tss.cms.entity.ChannelDistribute;
import com.jinhe.tss.cms.entity.ChannelDistributeId;
import com.jinhe.tss.cms.entity.WordsFilter;
import com.jinhe.tss.cms.helper.ArticleHelper;
import com.jinhe.tss.cms.helper.translator.ChannelCanSelectTranslator;
import com.jinhe.tss.cms.helper.translator.ExcludeSiteNodeTranslator;
import com.jinhe.tss.cms.publish.PublishManger;
import com.jinhe.tss.cms.service.IChannelService;
import com.jinhe.tss.component.recycle.RecycleInterceptor;
import com.jinhe.tss.core.exception.BusinessException;
import com.jinhe.tss.core.util.BeanUtil;
import com.jinhe.tss.core.util.EasyUtils;
import com.jinhe.tss.core.web.dispaly.tree.LevelTreeParser;
import com.jinhe.tss.core.web.dispaly.tree.TreeEncoder;

public class ChannelService implements IChannelService {

    @Autowired private IChannelDao channelDao;
    @Autowired private IArticleDao articleDao;
     
    public List<?> getAllSiteChannelList() {
        return channelDao.getAllSiteChannelList();
    }
    
    public List<?> getAllChannels() {
        String hql = "from Channel c where c.isSite = ? order by c.decode";
        return channelDao.getEntities(hql, CMSConstants.FALSE);
    }
    
    public Channel getSiteById(Long siteId) {
        Channel site = channelDao.getEntity( siteId );
        if (site == null) {
            throw new BusinessException("您选中站点不存在，可能已经被删除掉了！");
        }

        return site;
    }
    
	public Channel getChannelById(Long id) {
        Channel channel = channelDao.getEntity(id);
        if (channel == null) {
            throw new BusinessException("选择栏目不存在！id = " + id);
        }
        
        Long typeId = channel.getArticleTypeId();
        if ( typeId != null ) {
            ArticleType type = (ArticleType) channelDao.getEntity(ArticleType.class, typeId);
            if (type != null) {
                channel.setArticleTypeName(type.getName());
            }
        }
        return channel;
	}
	
	public Channel createChannel(Channel channel) {
        if (CMSConstants.SITE_DEFAULT_NO_RULE.equals(channel.getHotArticleRule()))
            channel.setHotArticleRule(CMSConstants.SITE_DEFAULT_HOT_ARTICLE_RULE);
        if (CMSConstants.SITE_DEFAULT_NO_RULE.equals(channel.getNewArticleRule())) 
            channel.setNewArticleRule(CMSConstants.SITE_DEFAULT_NEW_ARTICLE_RULE);
        
        channel.setSeqNo(channelDao.getNextSeqNo(channel.getParentId()));
        channel = channelDao.create(channel);
	    return channel;
    }
	
	public Channel updateChannel(Channel channel) {
		channelDao.update(channel);
	    return channel;
    }
	
    public Channel createSite(Channel site) {
        checkPath(site.getPath(), site.getDocPath(), site.getImagePath());
        site.setSeqNo(channelDao.getNextSeqNo(CMSConstants.HEAD_NODE_ID));
        site = channelDao.create(site);
        return site;
    }
    
    public Channel updateSite(Channel site) {
        checkPath(site.getPath(), site.getDocPath(), site.getImagePath());
        channelDao.update(site);
        return site;
    }
    
    /** 检测输入路径的正确性 */
    private void checkPath(String path, String docPath, String imgPath) {
        checkPath(path,    "站点的发布路径填写错误，不能生成相应的发布文件路径！");
        checkPath(docPath, "站点的附件上传根路径填写错误，不能生成相应的附件上传根路径路径！");
        checkPath(imgPath, "站点的图片根路径填写错误，不能生成相应的图片根路径路径！");
    }
    
    private void checkPath(String path, String errorMSg){
        File file = new File(path);
        if (!(file.exists() == true && file.isDirectory() == true && file.canWrite() == true)
                && !(file.exists() == false && file.mkdirs() == true)) {
            throw new BusinessException(errorMSg);
        }
    }
 
	public void deleteChannel(Long channelId) {
		if (!checkDownPermission(channelId, CMSConstants.OPERATION_DELETE)) {
			throw new BusinessException("您没有删除该栏目的足够权限！");
		}
		Channel channel = channelDao.getEntity(channelId);
		Channel site = channelDao.getSiteByChannel(channelId);
		
		// 先删除栏目下文章
        String hql = "select a, ca from Article a, ChannelArticle ca, Channel c " +
        		" where a.id = ca.id.articleId and ca.id.channelId = c.id and c.decode like ?";
        List<?> articleList = channelDao.getEntities(hql, channel.getDecode() + "%");
		for ( Object temp : articleList ) {
			Object[] objs = (Object[]) temp;
			Article article = (Article) objs[0];
			ChannelArticle channelArticle = (ChannelArticle) objs[1];
            
			// 相关文章处理(非分发文章)，如果是分发文章，则说明文章来自其他栏目，这里不做删除
			if ( !CMSConstants.ARTICLE_DISTRIBUTE.equals(channelArticle.getArticleOrigin()) ) {
				channelDao.deleteAll(channelDao.getEntities("from ArticleLation o where o.id.articleId = ?", article.getId()));
				
				// 需要绕过回收站拦截器直接物理删除文章，因为栏目已被删除，放入回收站也无意义（无法还原）
				RecycleInterceptor.switchTL.set(RecycleInterceptor.SWITCH_CLOSE_TAG);
				article.setSite(site);
				articleDao.deleteArticle(article);
			}
			channelDao.delete(channelArticle);
		}
		
        // 删除自己和子栏目
		List<Channel> subChannels = channelDao.getChildrenById(channelId);
		for(Channel temp : subChannels) {
			channelDao.deleteById(temp.getId());
		}
	}
	
    /** 对栏目向下权限的判断: 如果个数不相等，说明用户某些子节点不具有指定的权限 */
    protected boolean checkDownPermission(Long resourceId, String operationId) {
        List<Channel> rsourceList  = channelDao.getChildrenById(resourceId);
        List<?> permitedList = channelDao.getChildrenById(resourceId, operationId);

        return rsourceList.size() == permitedList.size();
    }
 
	public void sortChannel(Long channelId, Long toChannelId, Integer direction) {
        channelDao.sort(channelId, toChannelId, direction);
    }
 
    public void moveChannel(Long channelId, Long targetId) {
        Channel channel = channelDao.getEntity(channelId);
        Channel target  = channelDao.getEntity(targetId);
        channel.setSeqNo(channelDao.getNextSeqNo(targetId));
        channel.setParentId(targetId);
        
        channelDao.moveChannel(channel);
        
        List<Channel> children = channelDao.getChildrenById(channelId);
        for ( Channel temp : children ) {
            temp.setSiteId(target.isSite() ? target.getId() : target.getSiteId());
            temp.setDisabled(target.getDisabled());
 
            channelDao.update(temp);
        }
    }
 
	public List<Channel> copyChannel(Long channelId, Long targetId) {
        Channel channel = getChannelById(channelId);
		Channel target  = channelDao.getEntity(targetId); // 可能为空，复制整站时targetId=-1
		
		Long siteId = null;
        
        List<Channel> returnList = new ArrayList<Channel>();
        Map<Long, Long> idsMapping = new HashMap<Long, Long>(); // 维护源节点和复制节点间的映射关系
        
        List<Channel> children = channelDao.getChildrenById(channelId, CMSConstants.OPERATION_VIEW);
		for( Channel child : children ){
            Channel copyed = new Channel();
			BeanUtil.copy(copyed, child);
			copyed.setId(null);
            
            if(child.getId().equals(channelId)) {
                copyed.setParentId(targetId);
                copyed.setSeqNo(channelDao.getNextSeqNo(targetId));
                if ( child.getParentId().equals(targetId) ) {
                    copyed.setName(CMSConstants.COPY_NAME_PREFIX.concat(channel.getName()));
                } 
            } 
            else {
                copyed.setParentId( idsMapping.get(copyed.getParentId()) );
            }
            
            copyed = channelDao.create(copyed);
            
            if(target != null) {
                siteId = targetId;
                copyed.setDisabled(target.getDisabled());
            } 
            else { // target实体不存在，说明是整站复制（即在根节点下的复制），children里第一个节点即为站点
                if(copyed.isSite()) {
                    siteId = copyed.getId();
                }
            }
            copyed.setSiteId(siteId);
            
			idsMapping.put(child.getId(), copyed.getId());
			returnList.add(copyed);
		}
		channelDao.flush();
 
		return returnList;
	}
	
    public void stopSite(Long siteOrChannelId) {
        if (!checkDownPermission(siteOrChannelId, CMSConstants.OPERATION_STOP)) {
            throw new BusinessException("栏目没有停用权限！");
        }
 
        List<Channel> list = channelDao.getChildrenById(siteOrChannelId);
        for ( Channel child : list ) {
            child.setDisabled(CMSConstants.STATUS_STOP);
        }
        channelDao.flush();
    }

    public void startSiteAll(Long siteId) {
        Channel channel = getSiteById(siteId);
        
        // 启用站点
        channel.setDisabled(CMSConstants.STATUS_START);
        
        // 启用站点下栏目
        if (channel.isSite()) {
            List<?> list = channelDao.getChannelsBySiteIdNoPermission(siteId);
            for ( Object entity : list ) {
                Channel temp = (Channel) entity;
                temp.setDisabled(CMSConstants.STATUS_START);
            }
        } 
        channelDao.flush();
    }
 
    public void startChannel(Long channelId) {
        // 启用所有父亲节点
        List<Channel> parents = channelDao.getParentsById(channelId, CMSConstants.OPERATION_START);
        for ( Channel parent : parents ) {
            parent.setDisabled(CMSConstants.STATUS_START);
        }
        channelDao.flush();
    }
 
    public Object[] selectCanAddArticleParentChannels() {
        return selectParentChannelsByOperationId(CMSConstants.OPERATION_ADD_ARTICLE); // 新建文章
    }
    
    public Object[] selectCanAddChannelParentChannels() { 
        return selectParentChannelsByOperationId(CMSConstants.OPERATION_ADD_CHANNEL); // 新建栏目
    }

    private Object[] selectParentChannelsByOperationId(String operationId) {
        List<Long> canAddChannelIds = channelDao.getSiteChannelIDsByOperationId(operationId); 
        channelDao.insertIds2TempTable( canAddChannelIds );
        List<?> channels = channelDao.getParentChannel4CanAdd();
        return new Object[]{channels, EasyUtils.list2Str(canAddChannelIds)};
    }
    
    public List<Channel> getChannelTreeDown(Long channelId) {
        return channelDao.getChildrenById(channelId, CMSConstants.OPERATION_VIEW);
    }

    public void saveWordsFilter(String shieldWords, String forbidWords, Long siteId) {
        channelDao.createObject(new WordsFilter(siteId, shieldWords, forbidWords));
    }

    public WordsFilter getWordsFilterInfo(Long siteId) {
        return (WordsFilter) channelDao.getEntity( WordsFilter.class, siteId);
    }
 
	public Object[] getChannelRelationShip(Long channelId) {
		List<?> channelList = channelDao.getAllSiteChannelList();
	 	List<?> canAddArticleChannelIds = channelDao.getSiteChannelIDsByOperationId(CMSConstants.OPERATION_ADD_ARTICLE);
    	String channelIds = EasyUtils.list2Str(canAddArticleChannelIds);

		TreeEncoder allTreeEncoder = new TreeEncoder(channelList, new LevelTreeParser());
		allTreeEncoder.setTranslator(new ChannelCanSelectTranslator(channelIds, channelId));
        allTreeEncoder.setNeedRootNode(false);

		List<?> sourceList = channelDao.getChannelSourceByChannelId(channelId);
		TreeEncoder fromEncoder = new TreeEncoder(sourceList);
		fromEncoder.setRootNodeName("分发自");
		fromEncoder.setTranslator(new ExcludeSiteNodeTranslator());
		
        List<?> desList = channelDao.getChannelDestinationByChannelId(channelId);
		TreeEncoder toEncoder = new TreeEncoder(desList);
		toEncoder.setRootNodeName("分发至");
		toEncoder.setTranslator(new ExcludeSiteNodeTranslator());
        
		return new Object[] {allTreeEncoder, fromEncoder, toEncoder};
	}
 
	public void saveChannelRelationShip(Long channelId, String distributeFromIds, String distributeToIds) {
		// 保存栏目的源栏目信息
        channelDao.deleteAll(channelDao.getDistributeFromByChannelId(channelId));
 
        if ( !EasyUtils.isNullOrEmpty(distributeFromIds) ) {
            String[] sourceIds = distributeFromIds.split(",");
            for (int j = 0; j < sourceIds.length; j++) {
                Long sourceId = new Long(sourceIds[j]);
                insertChannelDistribute(channelId, sourceId);
            }
        }
        
		// 保存栏目的目的栏目信息
        channelDao.deleteAll(channelDao.getDistributeToByChannelId(channelId));
        if ( !EasyUtils.isNullOrEmpty(distributeToIds) ) {
            String[] desIds = distributeToIds.split(",");
            for (int i = 0; i < desIds.length; i++) {
                Long desId = new Long(desIds[i]);
                insertChannelDistribute(desId, channelId);
            }
        }
	}
	
    //添加分发栏目
    private void insertChannelDistribute(Long channelId, Long distributeFromId) {
        ChannelDistributeId channelDistributeId = new ChannelDistributeId();
        channelDistributeId.setDistributeToId(channelId);
        channelDistributeId.setDistributeFromId(distributeFromId);
        ChannelDistribute channelDispatch = new ChannelDistribute();
        channelDispatch.setId(channelDistributeId);
        channelDao.createObject(channelDispatch);
    }
    
    /**************************************************** 以下为栏目（站点）文章发布 *************************************************/

	public void publishArticle(List<?> articleIdList, Long siteId) {
        // 得到发布路径
        Channel site = (Channel)getChannelById(siteId);
        
        // 得到敏感词汇
        WordsFilter wordsFilter = (WordsFilter)channelDao.getEntity(WordsFilter.class, siteId);
		for (int i = 0; i < articleIdList.size(); i++) {
			// 取文章并设置文章属性
			Object[] objs = (Object[]) articleIdList.get(i);
			
            // 发布的时候需要把动态属性的字段内容也发布出去
			Article article = articleDao.getEntity((Long) objs[0]);
			setArticleValue(objs, article);
			
			// 取文章附件列表
			Map<String, Attachment> attachments = articleDao.getArticleAttachments(article.getId());
			article.getAttachments().putAll(attachments);
			
			// 发布文章，根据文章 创建日期 来设置xml文件的存放路径
            String publishPath = site.getPath()+ "/" + ArticleHelper.getArticlePublishRule(article.getCreateTime());
			String pubUrl = PublishManger.publishOneArticle(article, publishPath, wordsFilter);
			
			// 设置发布路径保存文章信息
			article.setPubUrl(pubUrl);
			articleDao.renewArticle(article);
		}
	}

	/**
	 * 设置文章相关属性，包括文章所属的栏目、文章类型、发布类路径等信息。
	 */
	private void setArticleValue(Object[] obj, Article article) {
		article.setChannelId((Long) obj[1]);
		article.setChannelName((String) obj[2]);
		article.setTypeName((String) obj[3]);
		article.setPublishArticleClassName((String) obj[4]);
		article.setStatus(CMSConstants.XML_STATUS);
		if (article.getIssueDate() == null || !article.getIssueDate().after(new Date())) {
			article.setIssueDate(new Date());
		}
	}

    public int getTotalRows4Publish(Long channelId, String category ) {
        return channelDao.getTotalRows4Publish(channelId, category);
    }
    
    public List<?> getPageArticleList(Long channelId, int pageNum, int pageSize, String category) {
        return channelDao.getPageArticleList4Publish(channelId, category, pageNum, pageSize);
    }
    
    public Integer getPublishableArticleCount(Long channelId) {
        return channelDao.getPublishableArticleCount(channelId);
    }

    public List<?> getPagePublishableArticleList(Long channelId, int currentPageNum, Integer pageSize) {
        return channelDao.getPagePublishableArticleList(channelId, currentPageNum, pageSize);
    }
    
    public Long getSiteIdByChannelId(Long channelId) {
        Channel channel = getChannelById(channelId);
        return channel.isSite() ? channel.getId() : channel.getSiteId();
    }
}